﻿using System;
using System.Configuration;
using System.Linq.Expressions;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Security;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Ninject.Core;
using Ninject.Core.Activation;
using Ninject.Core.Creation;
using Ninject.Core.Creation.Providers;
using Nvigorate.Common;
using Nvigorate.Common.Reflection;
using Nvigorate.Logging;
using Nvigorate.ServiceProxy;

namespace Nvigorate.Test
{
    /// <summary>
    /// Summary description for ProxyTests
    /// </summary>
    [TestClass]
    public class ProxyTests
    {
        private static ServiceHost _host;

        public ProxyTests()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        private static string _mex = @"net.tcp://localhost/testservice";
        private static string _address = @"http://localhost/testservice";
        private static ServiceEndpoint _endPoint;

        [ClassInitialize]
        public static void InitializeService(TestContext testContext)
        {
            _host = new ServiceHost(typeof(TestService)); 
            _host.CloseTimeout = TimeSpan.FromMilliseconds(100);
            _endPoint = _host.AddServiceEndpoint(typeof(ITestService), new BasicHttpBinding(BasicHttpSecurityMode.None),
                                     _address);
            _host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true, HttpGetUrl = new Uri(_address) });
            

            _host.Open();
        }

        public static void Nothing(object state)
        {
            
        }

        public static void StartHost()
        {
            
        }


        [ClassCleanup]
        public static void ShutDownService()
        {
            _host.Close();
            (_host as IDisposable).Dispose();
            GC.Collect();
        }

        [TestMethod]
        public void TestProxyCall()
        {
            using (var client = new ServiceClient<ITestService>(_address))
            {
                var returned = client.Proxy.ReturnString("test");
                Assert.AreEqual("test", returned);
            }
        }

        [TestMethod]
        public void TestProxyLambda()
        {
            using (var client = new ServiceClient<ITestService>(_address).AllTimeouts(TimeSpan.FromSeconds(2)))
            {
                var returned = client.Call(c => c.ReturnString("test"));
                Assert.AreEqual("test", returned);
            }
        }

        [TestMethod]
        public void TestModule()
        {
            var kernel = new ServiceKernel();
            var test = kernel.Get<ServiceClient<ITestService>>();
            Assert.IsNotNull(test);
        }
    }
    
    [ServiceContract]
    public interface ITestService
    {
        [OperationContract]
        string ReturnString(string input);
    }

    public class ServiceKernel : StandardKernel
    {
        public ServiceKernel() : base (new ServiceModule())
        {}
    }

    public class ServiceModule : StandardModule
    {
        public override void Load()
        {
            Bind(typeof (ServiceClient<>)).ToProvider(typeof (ServiceProvider));
        }
    }

    public class ServiceProvider : IProvider
    {
        
        public object Create(IContext context)
        {
            var types = GetContractType(context);
            return Reflector.CallMethod(types.Value2, "FromConfigruation", GetEndPoint(types.Value1));  
        }

        private Tuple<Type, Type> GetContractType(IContext context)
        {
            var contractType = context.GenericArguments[0];
            var targetType = typeof (ServiceClient<>).MakeGenericType(contractType);
            return Tuple.Create(contractType, targetType);
        }

        protected Type DoGetImplementationType(IContext context)
        {
            var types = GetContractType(context);
            return typeof (ServiceClient<>);
        }

        private string GetEndPoint(Type contractType)
        {
            ClientSection clientSection =
                ConfigurationManager.GetSection("system.serviceModel/client") as ClientSection;

            var endpoints =
                clientSection.ElementInformation.Properties[string.Empty].Value as ChannelEndpointElementCollection;

            foreach (ChannelEndpointElement endpoint in endpoints)
            {
                if (endpoint.Contract.Equals(contractType.FullName))
                    return endpoint.Name;
            }
            return "";
        }

        public Type GetImplementationType(IContext context)
        {
            return DoGetImplementationType(context);
        }

        public Type Prototype
        {
            get { return typeof(ServiceClient<>); }
        }

        public bool IsCompatibleWith(IContext context)
        {
            return true;
        }
    }
 
    public class TestService : ITestService
    {
        public string ReturnString(string input)
        {
            return input;
        }
    }
}
